package com.mzl.lal4cloud.lxw4rf.service.contract.service.impl;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mzl.lal4cloud.lxw4rf.service.contract.common.ApiUrl;
import com.mzl.lal4cloud.lxw4rf.service.contract.common.Consts;
import com.mzl.lal4cloud.lxw4rf.service.contract.config.ConfigStorage;
import com.mzl.lal4cloud.lxw4rf.service.contract.exception.ErrorException;
import com.mzl.lal4cloud.lxw4rf.service.contract.http.HttpType;
import com.mzl.lal4cloud.lxw4rf.service.contract.http.okhttp.OkHttpProxyInfo;
import okhttp3.*;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;

/**
 * okhttp实现.
 *
 * @author J_luo
 */
public class ServiceOkHttpImpl extends BaseServiceImpl<OkHttpClient, OkHttpProxyInfo> {
    private OkHttpClient httpClient;
    private OkHttpProxyInfo httpProxy;

    private final ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public OkHttpClient getRequestHttpClient() {
        return httpClient;
    }

    @Override
    public OkHttpProxyInfo getRequestHttpProxy() {
        return httpProxy;
    }

    @Override
    public HttpType getRequestType() {
        return HttpType.OK_HTTP;
    }

    @Override
    public String getAccessToken(boolean forceRefresh) throws ErrorException {
        final ConfigStorage config = this.getConfigStorage();
        if (!config.isAccessTokenExpired() && !forceRefresh) {
            return config.getAccessToken();
        }

        Lock lock = config.getAccessTokenLock();
        boolean locked = false;
        try {
            do {
                locked = lock.tryLock(100, TimeUnit.MILLISECONDS);
                if (!forceRefresh && !config.isAccessTokenExpired()) {
                    return config.getAccessToken();
                }
            } while (!locked);
            String url = ApiUrl.Auth.GET_TOKEN.getUrl(config);

            Map<String, String> requestData = new HashMap<>(2);
            requestData.put("clientId", config.getClientId());
            requestData.put("clientSecret", config.getClientSecret());
            final RequestBody requestBody = RequestBody.create(Consts.JSON_TYPE, objectMapper.writeValueAsString(requestData));

            Request request = new Request.Builder().url(url).post(requestBody).build();
            Response response = getRequestHttpClient().newCall(request).execute();
            return this.extractAccessToken(Objects.requireNonNull(response.body()).string());
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            if (locked) {
                lock.unlock();
            }
        }
    }

    @Override
    public void initHttp() {
        ConfigStorage configStorage = getConfigStorage();
        //设置代理
        if (configStorage.getHttpProxyHost() != null && configStorage.getHttpProxyPort() > 0) {
            httpProxy = OkHttpProxyInfo.httpProxy(configStorage.getHttpProxyHost(),
                    configStorage.getHttpProxyPort(),
                    configStorage.getHttpProxyUsername(),
                    configStorage.getHttpProxyPassword());
        }

        OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
        if (httpProxy != null) {
            clientBuilder.proxy(getRequestHttpProxy().getProxy());

            //设置授权
            clientBuilder.authenticator((route, response) -> {
                String credential = Credentials.basic(httpProxy.getProxyUsername(), httpProxy.getProxyPassword());
                return response.request().newBuilder()
                        .header("Authorization", credential)
                        .build();
            });
        }
        httpClient = clientBuilder.build();
    }

}
